package sm.hibernate.loader;

import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.engine.QueryParameters;
import org.hibernate.engine.SessionFactoryImplementor;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.loader.JoinWalker;
import org.hibernate.loader.collection.BasicCollectionLoader;
import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.type.ComponentType;
import org.hibernate.type.Type;

public class SMCollectionLoader extends BasicCollectionLoader {
	private static final Log log = LogFactory
			.getLog(BasicCollectionLoader.class);

	public SMCollectionLoader(QueryableCollection collectionPersister,
			SessionFactoryImplementor session, Map enabledFilters)
			throws MappingException {
		this(collectionPersister, 1, session, enabledFilters);
	}

	public SMCollectionLoader(QueryableCollection collectionPersister,
			int batchSize, SessionFactoryImplementor factory, Map enabledFilters)
			throws MappingException {
		this(collectionPersister, batchSize, null, factory, enabledFilters);
	}

	protected SMCollectionLoader(QueryableCollection collectionPersister,
			int batchSize, String subquery, SessionFactoryImplementor factory,
			Map enabledFilters) throws MappingException {

		super(collectionPersister, factory, enabledFilters);

		JoinWalker walker = new SMCollectionJoinWalker(collectionPersister,
				batchSize, subquery, factory, enabledFilters);
		initFromWalker(walker);

		postInstantiate();

		log.debug("Static select for collection "
				+ collectionPersister.getRole() + ": " + getSQLString());
	}

	@Override
	protected int bindPositionalParameters(final PreparedStatement statement,
			final QueryParameters queryParameters, final int startIndex,
			final SessionImplementor session) throws SQLException,
			HibernateException {
		final Object[] values = queryParameters
				.getFilteredPositionalParameterValues();
		final Type[] types = queryParameters
				.getFilteredPositionalParameterTypes();
		int span = 0;
		for (int i = 0; i < values.length; i++) {
			// customize: handle null composite key, set ? twice
			Type identifierType = types[i];
			int identifierColumnSpan = types[i].getColumnSpan(getFactory());
			if (identifierType instanceof ComponentType) {
				ComponentType componentIdType = (ComponentType) identifierType;
				Type[] propertyTypes = componentIdType.getSubtypes();
				Object[] subvalues = componentIdType.getPropertyValues(
						values[i], session.getEntityMode());
				for (int j = 0; j < identifierColumnSpan; j++) {
					int columnSpan = propertyTypes[j].getColumnSpan(session
							.getFactory());
					propertyTypes[j].nullSafeSet(statement, subvalues[j],
							startIndex + span, session);
					span += columnSpan;
					propertyTypes[j].nullSafeSet(statement, subvalues[j],
							startIndex + span, session);
					span += columnSpan;
				}
			} else {
				identifierType.nullSafeSet(statement, values[i], startIndex
						+ span, session);
				span += identifierColumnSpan;
			}
		}
		return span;
	}
}
